
#include <sys/types.h>
#include <sys/inttypes.h>

#include "atomic_op_namespace.h"

uint32_t _atomic_cas_32(volatile uint32_t *addr, uint32_t old, uint32_t new);
uint16_t _atomic_cas_16(volatile uint16_t *addr, uint16_t old, uint16_t new);
uint8_t _atomic_cas_8(volatile uint8_t *addr, uint8_t old, uint8_t new);

/*
 * This file provides emulation of 8 bit and 16 bit CAS operations based on
 * 32 bit CAS.
 */
uint16_t
_atomic_cas_16(volatile uint16_t *addr, uint16_t old, uint16_t new)
{
	const uintptr_t base = (uintptr_t)addr & ~3;
	const size_t off = (uintptr_t)addr - base;
	volatile uint32_t * ptr = (volatile uint32_t *)base;
	const size_t shift = off*8;
	const uint32_t mask = 0x0ffff << shift;
	const uint32_t old32_part = (uint32_t)old << shift;
	const uint32_t new32_part = (uint32_t)new << shift;
	uint32_t old32, new32;

	do {
		old32 = *ptr;
		if ((old32 & mask) != old32_part)
			return (uint16_t)((old32 & mask) >> shift);
		new32 = (old32 & ~mask) | new32_part;
	} while (_atomic_cas_32(ptr, old32, new32) != old32);

	return old;
}

crt_alias(__sync_val_compare_and_swap_2,_atomic_cas_16)

uint8_t
_atomic_cas_8(volatile uint8_t *addr, uint8_t old, uint8_t new)
{
	const uintptr_t base = (uintptr_t)addr & ~3;
	const size_t off = (uintptr_t)addr - base;
	volatile uint32_t * ptr = (volatile uint32_t *)base;
	const size_t shift = off*8;
	const uint32_t mask = 0x0ff << shift;
	const uint32_t old32_part = (uint32_t)old << shift;
	const uint32_t new32_part = (uint32_t)new << shift;
	uint32_t old32, new32;

	do {
		old32 = *ptr;
		if ((old32 & mask) != old32_part)
			return (uint8_t)((old32 & mask) >> shift);
		new32 = (old32 & ~mask) | new32_part;
	} while (_atomic_cas_32(ptr, old32, new32) != old32);

	return old;
}

crt_alias(__sync_val_compare_and_swap_1,_atomic_cas_8)
